/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.wsonrpc.json.support.gson;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.apexes.wsonrpc.core.exception.JsonException;
import net.apexes.wsonrpc.json.JsonImplementor;
import net.apexes.wsonrpc.json.JsonRpcError;
import net.apexes.wsonrpc.json.JsonRpcMessage;
import net.apexes.wsonrpc.json.JsonRpcRequest;
import net.apexes.wsonrpc.json.JsonRpcResponse;

/**
 * 
 * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
 */
public class GsonImplementor implements JsonImplementor {

    public static final GsonImplementor DEFAULT = new GsonImplementor();
    
    protected final Gson gson;
    
    public GsonImplementor() {
        this(new Gson());
    }
    
    public GsonImplementor(Gson gson) {
        this.gson = gson;
    }

    protected JsonRpcRequest createJsonRpcRequest(Gson gson, JsonObject jsonObject) {
        return new GsonJsonRpcRequest(gson, jsonObject);
    }

    protected JsonRpcResponse createJsonRpcResponse(Gson gson, JsonObject jsonObject) {
        return new GsonJsonRpcResponse(gson, jsonObject);
    }

    @Override
    public JsonRpcMessage fromJson(String json) throws JsonException {
        try {
            JsonObject jsonObject = (JsonObject) JsonParser.parseString(json);
            if (jsonObject.has("method")) {
                return createJsonRpcRequest(gson, jsonObject);
            }
            return createJsonRpcResponse(gson, jsonObject);
        } catch (Exception e) {
            throw new JsonException(e);
        }
    }

    @Override
    public JsonRpcRequest createRequest(String id, String method, Object[] params) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("jsonrpc", "2.0");
        jsonObject.addProperty("id", id);
        jsonObject.addProperty("method", method);
        if (params != null) {
            jsonObject.add("params", element(params));
        }
        return createJsonRpcRequest(gson, jsonObject);
    }

    @Override
    public JsonRpcRequest createNotice(String method, Object[] params, String trace) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("jsonrpc", "2.0");
        if (trace != null) {
            jsonObject.addProperty("trace", trace);
        }
        jsonObject.addProperty("method", method);
        if (params != null) {
            jsonObject.add("params", element(params));
        }
        return createJsonRpcRequest(gson, jsonObject);
    }

    @Override
    public JsonRpcResponse createResponse(String id, Object result) {
        return createResponse(id, "result", result);
    }

    @Override
    public JsonRpcResponse createResponse(JsonRpcError error) {
        return createResponse(null, error);
    }

    @Override
    public JsonRpcResponse createResponse(String id, JsonRpcError error) {
        return createResponse(id, "error", error);
    }

    private JsonRpcResponse createResponse(String id, String propertyName, Object object) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("jsonrpc", "2.0");
        if (id != null) {
            jsonObject.addProperty("id", id);
        }
        jsonObject.add(propertyName, element(object));
        return createJsonRpcResponse(gson, jsonObject);
    }

    private JsonElement element(Object object) {
        String paramsJson = gson.toJson(object);
        return JsonParser.parseString(paramsJson);
    }

}
